#pragma once
#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/json.h>
#include <iostream>
#include <memory>
#include "logger.hpp"


bool Serialize(const Json::Value &val, std::string& dst){
    Json::StreamWriterBuilder swb;
    swb.settings_["emitUTF8"] = true;

    // 构造对象
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    // auto sw = std::make_unique<Json::StreamWriter>(swb.newStreamWriter());
    std::stringstream ss;
    if(sw->write(val, &ss) != 0){
        std::cout << "Json 序列化失败 \n";
        return false;
    }
    dst = ss.str();
    return true;
}

bool UnSerialize(const std::string& src, Json::Value &val){
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    std::string err;
    if(!cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err)){
        std::cout << "Json 反序列化失败: " << err << std::endl; 
        return false;
    }
    return true;
}

// class ESIndex{
// public:
//     ESIndex(std::shared_ptr<elasticlient::Client> &client, 
//             const std::string &name, 
//             const std::string &type = "_doc"):
//             _name(name), _type(type), _client(client) 
//     {
//         // 拼装 settings 部分的 JSON
//         Json::Value analysis;
//         Json::Value analyzer;
//         Json::Value ik;
//         Json::Value tokenizer;
//         tokenizer["tokenizer"] = "ik_max_word"; // 使用中文分词
//         ik["ik"] = tokenizer;                   // {"ik":{"tokenizer":"ik_max_word"}}
//         analyzer["analyzer"] = ik;              // {"analyzer":{"ik":{...}}}
//         analysis["analysis"] = analyzer;        // {"analysis":{"analyzer":{...}}}
//         _index["settings"] = analysis;          // 根节点 _index 先放好 settings
//     }

//     // 提供链式 DSL写法，一口气把字段全部列完：
//     //   如: ESIndex(...).append("title").append("content", "text", "ik_smart", true).create();
//     ESIndex& append(const std::string &key, 
//         const std::string &type = "text", 
//         const std::string &analyzer = "ik_max_word", 
//         bool enabled = true) 
//     {
//         // 每个字段只维护 {"type":..., "analyzer":..., "enabled":...} 挂到 _properties[key] 下
//         Json::Value fields;
//         fields["type"] = type;
//         fields["analyzer"] = analyzer;
//         // 跳过该字段的倒排索引，节省磁盘但无法搜索，不要对需要检索的字段设置 false。
//         if (enabled == false) fields["enabled"] = enabled;
//         _properties[key] = fields;

//         // 返回 *this 实现链式调用，不用让用户多写临时变量
//         return *this;
//     }

//     bool create(const std::string &index_id = "default_index_id") 
//     {
//         // 1. 把 _properties 挂到 mappings.properties，再把 mappings 挂到 _index，形成完整请求体：
//         // settings + mappings → 发送 PUT /index_name/_doc/index_id
//         Json::Value mappings;
//         mappings["dynamic"] = true;          // 允许动态字段
//         mappings["properties"] = _properties;
//         _index["mappings"] = mappings;

//         std::string body;
//         if (!Serialize(_index, body)) {      // 把 Json 压成字符串
//             LOG_ERROR("索引序列化失败！");
//             return false;
//         }
//         LOG_DEBUG("{}", body);
//         // 2. 使用 elasticlient::Client->index() 发送 索引请求，成功返回 200-299，其余都算失败
//         try {
//             auto rsp = _client->index(_name, _type, index_id, body);
//             if (rsp.status_code < 200 || rsp.status_code >= 300) {
//                 LOG_ERROR("创建ES索引 {} 失败，响应状态码异常: {}", _name, rsp.status_code);
//                 return false;
//             }
//         } catch(std::exception &e) {
//             LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());
//             return false;
//         }
//         return true;
//     }

// private:
//     std::string _name;
//     std::string _type;
//     Json::Value _properties;
//     Json::Value _index;             // 用 _index 这个 Json::Value 当 总集装箱
//     std::shared_ptr<elasticlient::Client> _client;
// };

class ESIndex {
    public:
        ESIndex(std::shared_ptr<elasticlient::Client> &client, 
            const std::string &name, 
            const std::string &type = "_doc"):
            _name(name), _type(type), _client(client) {
            Json::Value analysis;
            Json::Value analyzer;
            Json::Value ik;
            Json::Value tokenizer;
            tokenizer["tokenizer"] = "ik_max_word";
            ik["ik"] = tokenizer;
            analyzer["analyzer"] = ik;
            analysis["analysis"] = analyzer;
            _index["settings"] = analysis;
        }
        ESIndex& append(const std::string &key, 
            const std::string &type = "text", 
            const std::string &analyzer = "ik_max_word", 
            bool enabled = true) {
            Json::Value fields;
            fields["type"] = type;
            fields["analyzer"] = analyzer;
            if (enabled == false ) fields["enabled"] = enabled;
            _properties[key] = fields;
            return *this;
        }
        bool create(const std::string &index_id = "default_index_id") {
            Json::Value mappings;
            mappings["dynamic"] = true;
            mappings["properties"] = _properties;
            _index["mappings"] = mappings;

            std::string body;
            bool ret = Serialize(_index, body);
            if (ret == false) {
                LOG_ERROR("索引序列化失败！");
                return false;
            }
            LOG_DEBUG("{}", body);
            //2. 发起搜索请求
            try {
                auto rsp = _client->index(_name, _type, index_id, body);
                if (rsp.status_code < 200 || rsp.status_code >= 300) {
                    LOG_ERROR("创建ES索引 {} 失败，响应状态码异常: {}", _name, rsp.status_code);
                    return false;
                }
            } catch(std::exception &e) {
                LOG_ERROR("创建ES索引 {} 失败: {}", _name, e.what());
                return false;
            }
            return true;
        }
    private:
        std::string _name;
        std::string _type;
        Json::Value _properties;
        Json::Value _index;
        std::shared_ptr<elasticlient::Client> _client;
};

class ESInsert{
public:
    ESInsert(std::shared_ptr<elasticlient::Client> &client, 
            const std::string &name, 
            const std::string &type = "_doc"):
            _name(name), _type(type), _client(client){}
    
    template<typename T>
    ESInsert &append(const std::string& key, const T& val){
        _item[key] = val;
        return *this;
    }

    bool insert(const std::string id = ""){
        std::string body;
        if(!Serialize(_item, body)){
            LOG_ERROR("索引序列化失败！");
            return false;
        }
        try{
            auto rsp = _client->index(_name, _type, id, body);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("新增数据 {} 失败，响应状态码异常: {}", body, rsp.status_code);
                return false;
            }
        }catch(std::exception& e){
            LOG_ERROR("新增数据 {} 失败: {}", body, e.what());
            return false;
        }  
        return true;
    }
    
private:
    std::string _name;
    std::string _type;
    Json::Value _item;
    std::shared_ptr<elasticlient::Client> _client;
};

class ESRemove{
public:
    ESRemove(std::shared_ptr<elasticlient::Client> &client, 
        const std::string &name, 
        const std::string &type = "_doc")
            :_name(name), _type(type), _client(client){}
    bool remove(const std::string &id){
        try {
            auto rsp = _client->remove(_name, _type, id);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("删除数据 {} 失败，响应状态码异常: {}", id, rsp.status_code);
                return false;
            }
        } catch(std::exception &e) {
            LOG_ERROR("删除数据 {} 失败: {}", id, e.what());
            return false;
        }
        return true;
    }
private:
    std::string _name;
    std::string _type;
    std::shared_ptr<elasticlient::Client> _client;
};


class ESSearch {
public:
    ESSearch(std::shared_ptr<elasticlient::Client> &client, 
        const std::string &name, 
        const std::string &type = "_doc"):
        _name(name), _type(type), _client(client){}
    ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {
        Json::Value fields;
        for (const auto& val : vals){
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        _must_not.append(terms);
        return *this;
    }
    ESSearch& append_should_match(const std::string &key, const std::string &val) {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _should.append(match);
        return *this;
    }
    ESSearch& append_must_term(const std::string &key, const std::string &val) {
        Json::Value field;
        field[key] = val;
        Json::Value term;
        term["term"] = field;
        _must.append(term);
        return *this;
    }
    ESSearch& append_must_match(const std::string &key, const std::string &val){
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _must.append(match);
        return *this;
    }

    Json::Value search(){
        Json::Value cond;
        if (_must_not.empty() == false) cond["must_not"] = _must_not;
        if (_should.empty() == false) cond["should"] = _should;
        if (_must.empty() == false) cond["must"] = _must;
        Json::Value query;
        query["bool"] = cond;
        Json::Value root;
        root["query"] = query;

        std::string body;
        if(!Serialize(root, body)){
            LOG_ERROR("索引序列化失败！");
            return Json::Value();
        }
        LOG_DEBUG("{}", body);

        // 搜索请求
        cpr::Response rsp;
        try{
            rsp = _client->search(_name, _type, body);
            if (rsp.status_code < 200 || rsp.status_code >= 300) {
                LOG_ERROR("检索数据 {} 失败，响应状态码异常: {}", body, rsp.status_code);
                return Json::Value();
            }
        }catch(std::exception &e){
            LOG_ERROR("检索数据 {} 失败: {}", body, e.what());
            return Json::Value();
        }
        // 对响应正文进行反序列化
        LOG_DEBUG("检索响应正文: [{}]", rsp.text);
        Json::Value json_res;
        if(!UnSerialize(rsp.text, json_res)){
            LOG_ERROR("检索数据 {} 反序列化失败", rsp.text);
            return Json::Value();
        }
        return json_res["hits"]["hits"];
    }
    
private:
        std::string _name;
        std::string _type;
        Json::Value _must_not;
        Json::Value _should;
        Json::Value _must;
        std::shared_ptr<elasticlient::Client> _client;
};

